package com.enlorenz.support.bank.iso8583;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.enlorenz.common.entity.PairEntity;
import com.enlorenz.common.util.CodeUtils;
import com.enlorenz.common.util.CommonContants;
import com.enlorenz.common.util.DataDetUtils;
import com.enlorenz.common.util.ReflectionUtils;
import com.enlorenz.common.util.StringUtils;
import com.enlorenz.common.util.XmlUtils;
import com.enlorenz.core.annotation.util.AnnotationUtils;
import com.enlorenz.core.annotation.util.AnnotationUtils.PropAnnoWrapper;
import com.enlorenz.core.enu.BaseEnum;
import com.enlorenz.support.bank.iso8583.msg.Iso8583Fields;
import com.solab.iso8583.IsoMessage;
import com.solab.iso8583.IsoType;
import com.solab.iso8583.MessageFactory;
import com.solab.iso8583.impl.SimpleTraceGenerator;
import com.solab.iso8583.parse.ConfigParser;

/**
 * 8583报文工具
 * @author Karma
 */
public class Iso8583Utils {
	//类型标签
	public static final String ISO_CONFIG_TAG_TYPE="type";
	public static final String ISO_CONFIG_TAG_CONFIG="j8583-config";
	public static final String ISO_CONFIG_TAG_PARSE="parse";
	public static final String ISO_CONFIG_TAG_TEMPLATE="template";
	public static final String ISO_CONFIG_TAG_HEADER="header";
	
	public static final String ISO_CONFIG_TAG_FIELD="field";
	public static final String ISO_CONFIG_TAG_NUM="num";
	public static final String ISO_CONFIG_TAG_CHILD_TYPE="type";
	public static final String ISO_CONFIG_TAG_LENGTH="length";
	
	
	/**
	 * 创建属性列表
	 * @param propList 属性列表
	 * @param xml xml缓存
	 */
	private static void createFieldList(List<PropAnnoWrapper<IsoProperty>> propList, StringBuffer xml) {
		for(PropAnnoWrapper<IsoProperty> pp:propList){
			XmlUtils.createPairTag(xml, ISO_CONFIG_TAG_FIELD,"0"
			,new PairEntity(ISO_CONFIG_TAG_NUM, pp.getAnnoObject().index())
			,new PairEntity(ISO_CONFIG_TAG_CHILD_TYPE,pp.getAnnoObject().isoType().name())
			,new PairEntity(ISO_CONFIG_TAG_LENGTH, pp.getAnnoObject().length()));
		}
	}
	
	
	/**
	 * 创建xml解析字符串
	 * @param type 报文类型
	 * @param claxx 类对象
	 * @return 解析配置
	 */
	@SuppressWarnings("rawtypes")
	public static String createParseXml(String type,Class claxx){
		List<PropAnnoWrapper<IsoProperty>> propList=AnnotationUtils.getAnnoList(claxx,IsoProperty.class,false);
		StringBuffer xml=new StringBuffer();
		xml.append(XmlUtils.XML_HEAD_UTF_8);
		XmlUtils.createStartTag(xml, ISO_CONFIG_TAG_CONFIG);
		XmlUtils.createPairTag(xml,ISO_CONFIG_TAG_HEADER,type
				,new PairEntity(ISO_CONFIG_TAG_TYPE, type));
		
		//创建模板
		createFieldPart(ISO_CONFIG_TAG_TEMPLATE,type,propList, xml);
		//创建解析
		createFieldPart(ISO_CONFIG_TAG_PARSE,type,propList, xml);
		
		XmlUtils.createEndTag(xml, ISO_CONFIG_TAG_CONFIG);
		return xml.toString();
	}
	
	/**
	 * 创建xml解析字符串
	 * @param type 报文类型
	 * @param claxx 类对象
	 * @return 解析配置
	 */
	@SuppressWarnings("rawtypes")
	public static String createParseXml(int type,Class claxx){
		return createParseXml(Integer.toHexString(type), claxx);
	}
	
	/**
	 * 创建域
	 * @param tagName
	 * @param type
	 * @param propList
	 * @param xml
	 */
	private static void createFieldPart(String tagName,String type,
			List<PropAnnoWrapper<IsoProperty>> propList, StringBuffer xml) {
		XmlUtils.createStartTag(xml,tagName
				,new PairEntity(ISO_CONFIG_TAG_TYPE, type));
		createFieldList(propList, xml);
		XmlUtils.createEndTag(xml,tagName);
	}
	
	
	/**
	 * 解析buff成iso对象
	 * @param xmlString xml模板字符串
	 * @param buff 数据buff
	 * @return iso对象
	 */
	public static IsoMessage parse(String xmlString,byte [] buff,int headLength){
		MessageFactory<IsoMessage> mfact = createMessageFactory(xmlString);
		return parse(mfact, buff,headLength);
	}

	/**
	 * 通过xml字符串创建消息工厂
	 * @param xmlString
	 * @return
	 * @throws IOException
	 */
	public static MessageFactory<IsoMessage> createMessageFactory(String xmlString){
		try {
			MessageFactory<IsoMessage> mfact = ConfigParser.
					createFromString(xmlString);
			mfact.setUseBinaryMessages(false);
			mfact.setUseBinaryBitmap(true);
			mfact.setForceSecondaryBitmap(true);
			return mfact;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	
	
	/**
	 * 解析buff成iso对象
	 * @param mfact 模板对象
	 * @param buff 数据buff
	 * @return iso对象
	 */
	public static IsoMessage parse(MessageFactory<IsoMessage> mfact,byte [] buff,int headLength){
		try {
			IsoMessage m = mfact.parseMessage(buff, headLength);
			return m;
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 创建默认的消息工厂
	 * @return 消息工厂
	 */
	public static MessageFactory<IsoMessage> createDefaultMsgFactory() {
		try {
			MessageFactory<IsoMessage> mfact = ConfigParser.createDefault();
			mfact.setAssignDate(true);
			mfact.setTraceNumberGenerator(stg);
			return mfact;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * //数值，0至9，右靠，首位有效数字前填零。若表示人民币金额，则最右二位为角、分
	NUMBER,
	//字母字符，A至Z，a至z，向左靠，右边多余位填空格
	ALPHA,
	//字母和数字字符，左靠，右边多余位填空格
	ALPHA_NUMBER,
	//字母、数字和特殊字符，左靠，右边多余位填空格
	ALPHA_NUMBER_SPACIAL,
	//无
	NONE
	 * @param value
	 * @param isoProperty
	 * @return
	 */
	public static Object getIsoData(Object object,String fieldName,IsoProperty isoProperty){
		Object value=ReflectionUtils.getValueFromField(fieldName, object);
		if(null == value){
			return value;
		}
		if(!isoProperty.isoType().equals(IsoType.ALPHA)){
			return value;
		}
		if(isoProperty.category().equals(IsoCategory.NUMBER)){
			String s=(String)value;
			return StringUtils.appendLeftZero(s, isoProperty.length());
		}if(isoProperty.category().equals(IsoCategory.ALPHA)
				||isoProperty.category().equals(IsoCategory.ALPHA_NUMBER)
				||isoProperty.category().equals(IsoCategory.ALPHA_NUMBER_SPACIAL)){
			String s=(String)value;
			return StringUtils.appendRightBlank(s, isoProperty.length());
		}
		return value;
	}
	
	/**
	 * 创建报文数据
	 * @param 命令类型
	 * @param data 数据类
	 * @return 数据buff
	 */
	public static byte [] create(int type,Object data,int headLength){
		return create(type, data, headLength, null);
	}
	
	public static interface IsoCreateHooker{
		void hook(IsoCreateWrapper isoCreateWrapper);
	}
	
	public static class IsoCreateWrapper{
		private int index;
		private Object value;
		private IsoType t;
		private int length;
		
		public int getIndex() {
			return index;
		}
		public void setIndex(int index) {
			this.index = index;
		}
		public Object getValue() {
			return value;
		}
		public void setValue(Object value) {
			this.value = value;
		}
		public IsoType getT() {
			return t;
		}
		public void setT(IsoType t) {
			this.t = t;
		}
		public int getLength() {
			return length;
		}
		public void setLength(int length) {
			this.length = length;
		}
	}

	@SuppressWarnings("rawtypes")
	public static byte [] create(int type,Object data,int headLength,IsoCreateHooker isoCreateHooker){
		MessageFactory<IsoMessage> mfact = createDefaultMsgFactory();
		IsoMessage isoMessage = mfact.newMessage(type);
		isoMessage.setBinary(false);
		//设置128位bitmap
		isoMessage.setForceSecondaryBitmap(true); 
		
		List<PropAnnoWrapper<IsoProperty>>
		propList=AnnotationUtils.getAnnoList(data.getClass(),IsoProperty.class,false);
		for(PropAnnoWrapper<IsoProperty> pp:propList){
			Object val=getIsoData(data,pp.getFieldName(),pp.getAnnoObject());
			if(null==val){
				continue;
			}
			if(val instanceof BaseEnum){
				val=((BaseEnum)val).getValue();
			}
			
			IsoCreateWrapper wrapper=new IsoCreateWrapper();
			wrapper.setIndex(pp.getAnnoObject().index());
			wrapper.setLength(pp.getAnnoObject().length());
			wrapper.setT(pp.getAnnoObject().isoType());
			wrapper.setValue(val);
			if(null!=isoCreateHooker){
				isoCreateHooker.hook(wrapper);
			}
			isoMessage.setValue(wrapper.getIndex(), wrapper.getValue(),
					wrapper.getT(),wrapper.getLength());
		}
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
		try {
			//报文长度为4
			isoMessage.write(baos,headLength);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return baos.toByteArray();
	}
	
	/**
	 * 打印Iso对象
	 * @param m
	 */
	public static void print(IsoMessage m) {
		System.out.println(getIsoDesc(m));
	}
	
	/**
	 * 获取iso对象描述
	 * @param m
	 * @return
	 */
	public static String getIsoDesc(IsoMessage m) {
		StringBuilder msg=new StringBuilder();
		msg.append("TYPE: ");
		msg.append(Integer.toHexString(m.getType()));
		msg.append(CommonContants.WRAP_SYMBOL);
		for (int i = 2; i <= 128; i++) {
			if (m.hasField(i)) {
				String template="F %s(%s): %s -> '%s'";
				msg.append(String.format(template,
						i,m.getField(i).getType()
						,m.getObjectValue(i)
						,m.getField(i).toString()));
				msg.append(CommonContants.WRAP_SYMBOL);
			}
		}
		return msg.toString();
	}
	
	/**
	 * 打印Iso对象
	 * @param m
	 */
	@SuppressWarnings("rawtypes")
	public static void print(Class claxx,IsoMessage m) {
		System.out.println(getIsoDesc(claxx,m));
	}
	@SuppressWarnings("rawtypes")
	public static String getIsoDesc(Class claxx,IsoMessage m){
		return getIsoDesc(new IsoConfig(claxx), m);
	}
	
	/**
	 * 打印Iso对象
	 * @param m
	 */
	public static void print(IsoConfig isoConfig,IsoMessage m) {
		System.out.println(getIsoDesc(isoConfig,m));
	}
	
	
	/**
	 * 解析iso消息到对象
	 * @param iso8583Fields iso对象
	 * @param isoConfig iso配置
	 * @param m iso消息
	 * @return 包含的域
	 */
	public static Set<Integer> parseIsoMessage2Object(Iso8583Fields iso8583Fields,
			IsoConfig isoConfig,IsoMessage m){
		iso8583Fields.setType(m.getType());
		Set<Integer> filedSet=new LinkedHashSet<Integer>();
		for (int i = 2; i <= 128; i++) {
			if (m.hasField(i)) {
				filedSet.add(i);
				iso8583Fields.setValue(i, m.getObjectValue(i));
			}
		}
		return filedSet;
	}
	
	public static void print(Iso8583Fields iso8583Fields){
		System.out.println(getIsoObjectDesc(iso8583Fields));
	}
	
	public static void print(Iso8583Fields iso8583Fields,IsoConfig isoConfig){
		System.out.println(getIsoObjectDesc(iso8583Fields, isoConfig));
	}
	
	public static String getIsoObjectDesc(Iso8583Fields iso8583Fields){
		return getIsoObjectDesc(iso8583Fields, Iso8583Fields.ISO_CONFIG);
	}
	

	public static String getIsoDesc(IsoConfig isoConfig,IsoMessage m){
		StringBuilder msg=new StringBuilder();
		msg.append("TYPE: ");
		msg.append(Integer.toHexString(m.getType()));
		msg.append(CommonContants.WRAP_SYMBOL);
		for (int i = 2; i <= 128; i++) {
			if (m.hasField(i)) {
				IsoWrapper iw=isoConfig.getIsoWrapperByIndex(i);
				String template="%s(%s),%s(%s):%s -> '%s'";
				msg.append(String.format(template
						,iw.getProperty().name()
						,iw.getFieldName()
						,i,m.getField(i).getType()
						,m.getObjectValue(i)
						,m.getField(i).toString()));
				msg.append(CommonContants.WRAP_SYMBOL);
			}
		}
		return msg.toString();
	}
	
	public static String getIsoObjectDesc(Iso8583Fields iso8583Fields,IsoConfig isoConfig){
		StringBuilder msg=new StringBuilder();
		msg.append("TYPE:");
		msg.append(Integer.toHexString(iso8583Fields.getType()));
		msg.append(CommonContants.WRAP_SYMBOL);
		
		for (Integer index:iso8583Fields.getFieldSet()) {
				IsoWrapper iw=isoConfig.getIsoWrapperByIndex(index);
				Object value=iso8583Fields.getValue(index);
				
				if(null!=value){
					if(value instanceof byte []){
						byte [] bval=(byte [])value;
						value=CodeUtils.bytes2HexString(bval);
					}
				}
				
				String template="%s(%s),%s(%s):'%s'";
				msg.append(String.format(template
						,iw.getProperty().name()
						,iw.getFieldName()
						,index
						,iw.getIsoProperty().isoType()
						,value));
				msg.append(CommonContants.WRAP_SYMBOL);
		}
		return msg.toString();
	}
	
	private static SimpleDateFormat datetimeFormat=new SimpleDateFormat("MMddHHmmss");
	/**
	 * 获取交易时间
	 * @return
	 */
	public static String getTransDatetime(){
		return datetimeFormat.format(new Date());
	}
	
	private static SimpleDateFormat localTimeFormat=new SimpleDateFormat("HHmmss");
	/**
	 * 获取本地时间
	 * @return
	 */
	public static String getLocaTime(){
		return localTimeFormat.format(new Date());
	}
	
	private static SimpleDateFormat localDateFormat=new SimpleDateFormat("MMdd");
	/**
	 * 获取本地日期
	 * @return
	 */
	public static String getLocaDate(){
		return localDateFormat.format(new Date());
	}
	
	private static SimpleTraceGenerator stg=new SimpleTraceGenerator((int) (System
			.currentTimeMillis() % 10000));
	
	/**
	 * 获取系统跟踪号
	 * @return
	 */
	public static String getSysTraceAuditNo(){
		return String.valueOf(stg.nextTrace());
	}
	
	/**
	 * 获取6位跟踪号
	 * @return
	 */
	public static String getSysTraceAuditNoSixLength(){
		String ta=getSysTraceAuditNo();
		return StringUtils.appendLeftZero(ta, 6);
	}
	
	
	/**
	 * 打印16进制字符串
	 * @param type 报文类型
	 * @param hexString 16进制字符串
	 */
	public static void printHexString(int type,String hexString,int headLength){
		byte [] buff=CodeUtils.hexString2Bytes(hexString);
		IsoMessage im=Iso8583Utils.parse(Iso8583Utils.
				createParseXml(type, Iso8583Fields.class),buff,headLength);
		Iso8583Utils.print(Iso8583Fields.class, im);
	}
	
	/**
	 * 解析单个属性域
	 * @param data 数据
	 * @param fieldClass 域类对象
	 * @param isoConfig iso配置
	 * @return
	 */
	public static <T> T parseSingleField(String data,Class<T> fieldClass,IsoConfig isoConfig){
		List<IsoWrapper> isoWrapperList=isoConfig.getIsoWrapperList();
		T obj=ReflectionUtils.newInstanceClass(fieldClass);
		int start=0;
		for(IsoWrapper iw:isoWrapperList){
			int length=iw.getIsoProperty().length();
			int end=start+length;
			String val=data.substring(start,end);
			ReflectionUtils.setValue2Field(iw.getFieldName(), 
					obj, val);
			start=end;
		}
		return obj;
	}
	
	/**
	 * 打包单独的域
	 * @param obj 数据对象
	 * @param isoConfig iso配置
	 * @return
	 */
	public static String packSingleField(Object obj,IsoConfig isoConfig){
		List<IsoWrapper> isoWrapperList=isoConfig.getIsoWrapperList();
		StringBuilder hexStr=new StringBuilder();
		for(IsoWrapper iw:isoWrapperList){
			Object value=getIsoData(obj, iw.getFieldName(), iw.getIsoProperty());
			hexStr.append(value);
		}
		return hexStr.toString();
	}
	
	public static final String RES_SUCCESS_CODE="00";
	
	/**
	 * 响应是否成功
	 * @param code 返回字符
	 * @return 是否成功
	 */
	public static boolean isResponseSuccess(String code){
		if(DataDetUtils.isStrEmpty(code)){
			return false;
		}
		return code.equals(RES_SUCCESS_CODE);
	}
	
	/**
	 * 响应是否成功
	 * @param iso8583Fields 域对象
	 * @return 是否成功
	 */
	public static boolean isResponseSuccess(Iso8583Fields iso8583Fields){
		if(null == iso8583Fields){
			return false;
		}
		return isResponseSuccess(iso8583Fields.getResCode());
	}
}
